using System;
using SharpMud.Net.Telnet.IAC;

namespace SharpMud.Net.Telnet.Session.NegotationAgents
{
	/// <summary>
	/// This is the negotiation daemon for NAWS.
	/// </summary>
	public class NAWS : IFeatureNegotiationAgent
	{
		private TelnetSessionBase	_HostSession;
		private bool				_CurrentlyAssuming;
		private System.Int16		_TerminalWidth;
		private System.Int16		_TerminalHeight;
		private FeatureNegotiationBehavior	_Behavior;
		private bool						_IsNegotiationRefused;

		#region Constructors
		private NAWS()
		{
		}
		public NAWS(TelnetSessionBase hostSession)
		{
			_Behavior = (FeatureNegotiationBehavior)(-1);
			
			_Enabled = false;
			_ChangesPending = 0;
			_HostSession = hostSession;
			_RequireClientVerification = true;
			
			_CurrentlyAssuming = true;
			_TerminalWidth = (System.Int16)80;
			_TerminalHeight = (System.Int16)24;

			hostSession.IAC.In.IAC_Feature_Negotiation +=new SharpMud.Net.Telnet.Session.DataProcessors.IAC.IACInputEventHandler(IAC_Feature_Negotiation);
			hostSession.IAC.In.IAC_Subrequest +=new SharpMud.Net.Telnet.Session.DataProcessors.IAC.IACInputEventHandler(IAC_Subrequest);
		}

		private bool _Enabled;

		private int _ChangesPending;
		public bool ChangePending
		{
			get
			{
				return (_ChangesPending>0);
			}
		}

		private bool _RequireClientVerification;
		public bool RequireClientVerification
		{
			get
			{
				return _RequireClientVerification;
			}
			set
			{
				_RequireClientVerification = value;
			}
		}
		#endregion

		private void IAC_Feature_Negotiation(SharpMud.Net.Telnet.Session.DataProcessors.IAC.InputLayer sender, SharpMud.Net.Telnet.Session.DataProcessors.IAC.IACInputEventArgs e)
		{
			if(e.FeatureType!=ControlCodes.TELOPT.EOR) return;
			
			if(e.NegotiationQuery == ControlCodes.DO)
			{
				//The client is saying it wants us to accept its NAWS.
				//Servers don't do that. Tell it NO! Bad client!
				_HostSession.IAC.Out.Send_FeatureNegotiation_Packet(ControlCodes.WONT,ControlCodes.TELOPT.NAWS);
				return;
			}
			if(e.NegotiationQuery == ControlCodes.DONT)
			{
				//The client is saying it doesn't want us to accept its NAWS.
				//Which is okay, since it isn't supported server side.
				return;
			}
			if(e.NegotiationQuery == ControlCodes.WILL)
			{
				if(!_Enabled)
					_Enabled = true;
				if(ChangePending)
					_ChangesPending--;
				return;
			}
			if(e.NegotiationQuery == ControlCodes.WONT)
			{
				if(_Enabled)
					_Enabled = false;
				if(ChangePending)
					_ChangesPending--;
				return;
			}
		}

		private void IAC_Subrequest(SharpMud.Net.Telnet.Session.DataProcessors.IAC.InputLayer sender, SharpMud.Net.Telnet.Session.DataProcessors.IAC.IACInputEventArgs e)
		{
			if((byte)e.FeatureType!=(byte)ControlCodes.TELOPT.NAWS) return;

			byte width_1 = e.SubrequestData[0];
			byte width_0 = e.SubrequestData[1];
			byte height_1 = e.SubrequestData[2];
			byte height_0 = e.SubrequestData[3];
			
			short width = Build_Int16(width_1,width_0);
			short height = Build_Int16(height_1,height_0);

			this._TerminalWidth = width;
			this._TerminalHeight = height;

			_CurrentlyAssuming = false;

			if(this.TerminalSizeChanged!=null)
				this.TerminalSizeChanged(this,System.EventArgs.Empty);
		}

		public short TerminalWidth
		{
			get
			{
				return this._TerminalWidth;
			}
		}

		public short TerminalHeight
		{
			get
			{
				return this._TerminalHeight;
			}
		}

		public bool CurrentlyAssuming
		{
			get
			{
				return _CurrentlyAssuming;
			}
		}

		private static System.Int16 Build_Int16(byte byte1, byte byte0)
		{
			return (short)( (((short)byte1)*((short)byte.MaxValue)) + ((short)byte0) );
		}

		public System.EventHandler TerminalSizeChanged;
		
		#region IFeatureNegotiationAgent Members

		public SharpMud.Net.Telnet.Session.FeatureNegotiationBehavior Behavior
		{
			get
			{
				return _Behavior;
			}
			set
			{
				if(value!=_Behavior)
				{
					if(value==FeatureNegotiationBehavior.DemandClientSupport && this.Status!=TriState.True)
					{
						this.TurnFeatureOn();
					}
					if(value==FeatureNegotiationBehavior.RejectClientOnDemand && this.Status!=TriState.False)
					{
						this.TurnFeatureOff();
					}
					if(value==FeatureNegotiationBehavior.AcceptClientOnDemand && this.Status!=TriState.False)
					{
						this.TurnFeatureOn();
					}
					_Behavior = value;
				}
			}
		}

		public System.TriState Status
		{
			get
			{
				if(_IsNegotiationRefused)
					return TriState.False;
				if(this._RequireClientVerification && this._ChangesPending>0)
					return TriState.UseDefault;
				if(this._Enabled)
					return TriState.True;
				else
					return TriState.False;
			}
		}

		public bool EffectiveStatus
		{
			get
			{
				return this._Enabled;
			}
		}

		public void TurnFeatureOn()
		{
			if(_Enabled==false && (ChangePending==false || RequireClientVerification==false))
			{
				//Need to turn remote NAWS support back on
				_ChangesPending++;
				this._HostSession.IAC.Out.Send_FeatureNegotiation_Packet(ControlCodes.DO,ControlCodes.TELOPT.NAWS);
				if(!RequireClientVerification)
				{
					_Enabled = true;
				}
				return;
			}
		}

		public void TurnFeatureOff()
		{
			if(_Enabled==true && (ChangePending==false || RequireClientVerification==false))
			{
				//Need to turn remote NAWS support off
				_ChangesPending++;
				this._HostSession.IAC.Out.Send_FeatureNegotiation_Packet(ControlCodes.DONT,ControlCodes.TELOPT.NAWS);
				if(!RequireClientVerification)
				{
					_Enabled = false;
				}
				return;
			}
		}

		#endregion
	}
}